/*
 * GMMMapper.h
 *
 *  Created on: Aug 3, 2011
 *      Author: yong
 */

#ifndef GMMMAPPER_H_
#define GMMMAPPER_H_
#include<iostream>
#include "Typedef.h"
#include "Page.h"
#include "Config.h"
#include "math/MultiGaussian.h"

namespace distrim
{

template<typename K, typename V1, typename V2>
class GMMMapper: public Mapper<K, V1, std::vector<
		boost::numeric::ublas::matrix<V2> > >
{
private:
	typedef boost::numeric::ublas::matrix<V2> Matrix;
	typedef std::vector<Matrix> MV;
	typedef std::vector<MultiGuassian<V2> > MGV;
	typedef std::vector<V2> WV;
public:
	GMMMapper(KeyValuePage<K, V1, MV> *ptrKeyValuePage, const Config &config,
			MV *ptrParam) :
		Mapper<K, V1, MV> (ptrKeyValuePage, config, ptrParam), m_clusterNum(
				config.GetClusterNum()), m_valueElemCount(
				config.GetValueElemCount()), m_vecCalculator(m_clusterNum + 1,
				MultiGuassian<V2> (m_valueElemCount)), m_vecPartialSum(3
				* m_clusterNum + 1), m_tV(1, m_valueElemCount), m_tA(
				m_valueElemCount, m_valueElemCount), m_ptrParam(ptrParam),
				m_weighteDen(m_clusterNum + 1, V2()), m_resetSize(sizeof(V2)
						* (m_clusterNum + 1))
	{
		setup();
	}
	virtual ~GMMMapper()
	{
	}
	virtual void Map(const KeyValue<K, V1> &keyValue)
	{
		// Get point vector as matrix.
		Util::KeyValueToMatrix(keyValue, m_tV);
		for (size_t c = 1; c < m_clusterNum + 1; ++c)
		{
			// Comput weighted density and sum all weighted density.
			m_weighteDen[c] = m_vecCalculator[c].GetProbability(m_tV)
					* (*m_ptrParam)[c](0, 0);
			m_weighteDen[0] += m_weighteDen[c];
		}
		// Add partial likelihood.
		m_vecPartialSum[0](0, 0) += LOG(m_weighteDen[0]);
		// Add partial gama, gamma * x, gamma * x^ T * x.
		for (size_t c = 1; c < m_clusterNum + 1; ++c)
		{
			V2 weight = m_weighteDen[c] / m_weighteDen[0];
			m_vecPartialSum[c](0, 0) += weight;
			m_vecPartialSum[c + m_clusterNum] += m_tV * weight;
			prod(trans(m_tV), m_tV, m_tA);
			m_vecPartialSum[c + 2 * m_clusterNum] += m_tA * weight;
		}
		std::fill(m_weighteDen.begin(), m_weighteDen.end(), V2());
	}

	inline const MV &GetPartialSum() const
	{
		return m_vecPartialSum;
	}

	inline void Reset()
	{
		// Reset weighted densities.
		std::fill(m_weighteDen.begin(), m_weighteDen.end(), V2());
		// Reset calculators.
		for (size_t c = 1; c < m_clusterNum + 1; ++c)
		{
			m_vecCalculator[c].SetMeanSigma((*m_ptrParam)[c + m_clusterNum],
					(*m_ptrParam)[c + 2 * m_clusterNum]);
		}
foreach	(Matrix &m, m_vecPartialSum)
	{
		m.clear();
	}
}

private:
inline void setup()
{
	// request space for likelihood.
	m_vecPartialSum[0] = Matrix(1, 1, V2());
	for (size_t c = 1; c < m_clusterNum + 1; ++c)
	{
		// request space for pi.
		m_vecPartialSum[c] = Matrix(1, 1, V2());
		// request space for mean.
		m_vecPartialSum[c + m_clusterNum] = Matrix(1, m_valueElemCount,
				V2());
		// request space for gama.
		m_vecPartialSum[c + 2 * m_clusterNum] = Matrix(m_valueElemCount,
				m_valueElemCount, V2());
	}
}
private:
size_t m_clusterNum;
size_t m_valueElemCount;
MGV m_vecCalculator;
MV m_vecPartialSum;
Matrix m_tV;
Matrix m_tA;
MV *m_ptrParam;
WV m_weighteDen;
size_t m_resetSize;
};

}

#endif /* GMMMAPPER_H_ */
